// https://juejin.cn/post/6844904184894980104
import type { FC } from 'react';

interface IProps {}

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
  if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''));
  } else if (typeof x === 'string') {
    return x.split('').reverse().join('');
  }
}
reverse(1);

// 泛型
// K（Key）：表示对象中的键类型；
// V（Value）：表示对象中的值类型；
// E（Element）：表示元素类型。
function identity<T, U>(num: T, age?: U): void {}
identity<number, string>(1);

interface ILength {
  length: number;
}
function identity2<T extends ILength>(num: T): void {
  console.log(num.length);
}
identity2<number[]>([1]);

interface Person {
  name: string;
  age: number;
  location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number

const k1: K1 = 'age';

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
getProperty({ name: 'zp' }, 'name');

// 默认是number类型
interface TestTs<T = number> {
  age: T;
}
const testTs: TestTs<string> = { age: '1' };

// 上例中，我们重复定义了多次函数 reverse，前几次都是函数定义，最后一次是函数实现。在编辑器的代码提示中，可以正确的看到前两个提示。
// 注意，TypeScript 会优先从最前面的函数定义开始匹配，所以多个函数定义如果有包含关系，需要优先把精确的定义写在前面。

const TypeOfFunction: FC<IProps> = (props) => {
  return <div>TypeOfFunction</div>;
};
export default TypeOfFunction;

// Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?。
interface Person2 {
  age: number;
  height: string;
}
type partialP2 = Partial<Person2>;
type selfPartial<T> = { [P in keyof T]?: T[P] };

// Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。
interface PageInfo {
  title: string;
}

type Page = 'home' | 'about' | 'contact';

const x: Record<Page, PageInfo> = {
  about: { title: 'contact' },
  contact: { title: 'contact' },
  home: { title: 'home' },
};

type selfRecord<K extends keyof any, T> = { [P in K]: T };

// Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来，变成包含这个类型部分属性的子类型。
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, 'title' | 'completed'>;

const todo: TodoPreview = {
  title: 'Clean room',
  completed: false,
};
type selfPick<T, K extends keyof T> = {
  [P in K]: T[K];
};

// Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉。
type T0 = Exclude<'a' | 'b' | 'c', 'a'>; // "b" | "c"
type selfExclude<T, U> = T extends U ? never : T;
type T1 = selfExclude<'a' | 'b' | 'c', 'a'>; // "b" | "c"

// ReturnType<T> 的作用是用于获取函数 T 的返回类型。
type User = {
  id?: string; // 用户id
  name?: string; // 用户名
  password?: string; // 密码
  createdAt: Date; // 创建时间
  updatedAt: Date; // 更新时间
};
type User2 = Omit<User, 'id' | 'name'>;
type selfOmit<T, K extends string | number | undefined> = {
  [P in Exclude<keyof T, K>]: T[P];
};

type User3 = Required<User>;
type selfRequired<T> = { [P in keyof T]-?: T[P] };

type User4 = Readonly<User>;
type selfReadOnly<T> = { readonly [P in keyof T]: T[P] };
